﻿// Decompiled with JetBrains decompiler
// Type: Microsoft.InfoCards.IndexedDataBuffer
// Assembly: infocard, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// MVID: 8E14765A-6610-409A-BA36-099A0642905D
// Assembly location: E:\git\ALLIDA\windll\infocard.exe

using Microsoft.InfoCards.Diagnostics;
using System;
using System.IO;
using System.Runtime.InteropServices;

namespace Microsoft.InfoCards
{
  internal class IndexedDataBuffer
  {
    private byte[] m_buffer;
    private byte[] m_masterIndex;
    private FreeIndexList m_freeList;
    private int m_dataLength;
    private bool m_isOpen;
    private DataSource m_owner;

    public unsafe IndexedDataBuffer(
      byte[] masterIndex,
      byte[] buffer,
      int dataLength,
      DataSource owner)
    {
      if (masterIndex == null || masterIndex.Length == 0)
        throw InfoCardTrace.ThrowHelperArgumentNull(nameof (masterIndex), SR.GetString("StoreIndexedDataBufferNullOrEmptyMasterIndexBuffer"));
      if (buffer == null || buffer.Length == 0)
        throw InfoCardTrace.ThrowHelperArgumentNull(nameof (buffer), SR.GetString("StoreIndexedDataBufferNullOrEmptyDataIndexBuffer"));
      if (dataLength < 0 || dataLength > buffer.Length)
        throw InfoCardTrace.ThrowHelperError((Exception) new ArgumentOutOfRangeException(nameof (dataLength), (object) dataLength, SR.GetString("StoreIndexDataBufferDataLengthOutOfRange")));
      this.m_owner = owner;
      this.m_masterIndex = masterIndex;
      this.m_buffer = buffer;
      this.m_dataLength = dataLength;
      this.m_freeList = new FreeIndexList(20);
      fixed (byte* numPtr = &this.m_masterIndex[0])
      {
        for (int index = 1; index < this.m_masterIndex.Length / 4; ++index)
        {
          if (((int*) numPtr)[index] <= 0)
            this.m_freeList.Put(index);
        }
      }
      this.m_isOpen = true;
    }

    public byte[] Index
    {
      get
      {
        this.ThrowIfNotOpen();
        return this.m_masterIndex;
      }
    }

    public byte[] Data
    {
      get
      {
        this.ThrowIfNotOpen();
        return this.m_buffer;
      }
    }

    public int DataLength
    {
      get
      {
        this.ThrowIfNotOpen();
        return this.m_dataLength;
      }
    }

    public void Close()
    {
      this.ThrowIfNotOpen();
      this.Clear();
      this.m_buffer = (byte[]) null;
      this.m_masterIndex = (byte[]) null;
      this.m_isOpen = false;
    }

    public unsafe DataRow CreateDataRow(int id)
    {
      this.ThrowIfNotOpen();
      if (id > this.m_masterIndex.Length / 4 || this.m_freeList.Contains(id))
        throw InfoCardTrace.ThrowHelperError((Exception) new ArgumentOutOfRangeException(nameof (id), (object) id, SR.GetString("StoreLocalIdOutOfRange")));
      fixed (byte* numPtr = &this.m_buffer[this.GetOffset(ref id)])
        return DataRow.Create((EncryptedObjectHeader*) numPtr, this.m_owner);
    }

    public unsafe void CopyIVFromObject(int id, byte[] iv, int index)
    {
      this.ThrowIfNotOpen();
      if (iv == null || iv.Length == 0)
        throw InfoCardTrace.ThrowHelperArgumentNull(nameof (iv));
      if (index < 0 || index > this.DataLength - iv.Length)
        throw InfoCardTrace.ThrowHelperError((Exception) new ArgumentOutOfRangeException(nameof (index), (object) index, SR.GetString("StoreIndexDataBufferIndexOutOfRange")));
      if (id > this.m_masterIndex.Length / 4 || this.m_freeList.Contains(id))
        throw InfoCardTrace.ThrowHelperError((Exception) new ArgumentOutOfRangeException(nameof (id), (object) id, SR.GetString("StoreLocalIdOutOfRange")));
      int offset = this.GetOffset(ref id);
      InfoCardTrace.Assert(id >= 0, "Invalid ID was returned by GetOffset.");
      fixed (byte* numPtr = &this.m_buffer[offset])
        Marshal.Copy(new IntPtr((void*) &((EncryptedObjectHeader*) numPtr)->IV), iv, 0, iv.Length);
    }

    public Stream GetStreamForObjectData(int id)
    {
      this.ThrowIfNotOpen();
      if (id > this.m_masterIndex.Length / 4 || this.m_freeList.Contains(id))
        throw InfoCardTrace.ThrowHelperError((Exception) new ArgumentOutOfRangeException(nameof (id), (object) id, SR.GetString("StoreLocalIdOutOfRange")));
      int offset = this.GetOffset(ref id);
      InfoCardTrace.Assert(id >= 0, "Invalid ID was returned by GetOffset.");
      int objectDataSize = this.GetObjectDataSize(offset);
      InfoCardTrace.Assert(objectDataSize <= this.m_buffer.Length - offset, "The length the stored object is invalid");
      return (Stream) new MemoryStream(this.m_buffer, offset + sizeof (EncryptedObjectHeader), objectDataSize, false, false);
    }

    public void RemoveObject(int id)
    {
      this.ThrowIfNotOpen();
      if (id > this.m_masterIndex.Length / 4 || this.m_freeList.Contains(id))
        throw InfoCardTrace.ThrowHelperError((Exception) new ArgumentOutOfRangeException(nameof (id), (object) id, SR.GetString("StoreLocalIdOutOfRange")));
      int offset = this.GetOffset(ref id);
      InfoCardTrace.Assert(id >= 0, "Invalid ID was returned by GetOffset.");
      this.RemoveFromMasterIndex(id);
      this.EnsureSpaceForData(offset, 0);
    }

    public unsafe int WriteObject(
      int id,
      byte[] iv,
      byte[] data,
      int objectType,
      long lastChange,
      GlobalId globalId)
    {
      this.ThrowIfNotOpen();
      if (iv == null || iv.Length == 0)
        throw InfoCardTrace.ThrowHelperArgumentNull(nameof (iv));
      if (data == null || data.Length == 0)
        throw InfoCardTrace.ThrowHelperArgumentNull(nameof (iv));
      if (id > this.m_masterIndex.Length / 4)
        throw InfoCardTrace.ThrowHelperError((Exception) new ArgumentOutOfRangeException(nameof (id), (object) id, SR.GetString("StoreLocalIdOutOfRange")));
      if (id > 0 && this.m_freeList.Contains(id))
        throw InfoCardTrace.ThrowHelperError((Exception) new ArgumentOutOfRangeException(nameof (id), (object) id, SR.GetString("StoreLocalIdOutOfRange")));
      int offset = this.GetOffset(ref id);
      InfoCardTrace.Assert(id >= 0, "Invalid ID was returned by GetOffset.");
      this.EnsureSpaceForData(offset, data.Length);
      fixed (byte* numPtr1 = &this.m_buffer[offset])
      {
        byte* numPtr2 = (byte*) ((EncryptedObjectHeader*) numPtr1 + 1);
        ((EncryptedObjectHeader*) numPtr1)->LastChange = lastChange;
        ((EncryptedObjectHeader*) numPtr1)->DataSize = data.Length;
        ((EncryptedObjectHeader*) numPtr1)->ObjectType = objectType;
        ((EncryptedObjectHeader*) numPtr1)->LocalId = id;
        ((EncryptedObjectHeader*) numPtr1)->GlobalId = globalId;
        InfoCardTrace.Assert(iv.Length <= 32, "IV Length is invalid");
        Marshal.Copy(iv, 0, new IntPtr((void*) &((EncryptedObjectHeader*) numPtr1)->IV), iv.Length);
        Marshal.Copy(data, 0, new IntPtr((void*) numPtr2), data.Length);
      }
      return id;
    }

    private unsafe int GetObjectDataSize(int offset)
    {
      fixed (byte* numPtr = &this.m_buffer[offset])
        return ((EncryptedObjectHeader*) numPtr)->DataSize;
    }

    private void Clear()
    {
      Array.Clear((Array) this.m_buffer, 0, this.m_dataLength);
      Array.Clear((Array) this.m_masterIndex, 0, this.m_masterIndex.Length);
      this.m_dataLength = 0;
    }

    private unsafe int EnsureSpaceForData(int offset, int dataSize)
    {
      InfoCardTrace.Assert((uint) offset <= (uint) this.m_dataLength, "offset specified is outside the range of the data buffer");
      InfoCardTrace.Assert(dataSize >= 0, "The requested data size must be greater than or equal to 0");
      int num1 = 0;
      if (dataSize != 0)
        num1 = dataSize + sizeof (EncryptedObjectHeader);
      if (num1 % 8 != 0)
        num1 += 8 - num1 % 8;
      int num2;
      if (offset == this.m_dataLength)
      {
        num2 = 0;
      }
      else
      {
        fixed (byte* numPtr = &this.m_buffer[offset])
        {
          num2 = ((EncryptedObjectHeader*) numPtr)->DataSize + sizeof (EncryptedObjectHeader);
          if (num2 % 8 != 0)
            num2 += 8 - num2 % 8;
        }
      }
      InfoCardTrace.Assert(num2 <= this.m_dataLength, "currentSize can not be more ");
      if (num2 == num1)
        return num1;
      int num3 = offset + num2;
      int diff = num1 - num2;
      int num4 = num3 + diff;
      if (diff > 0 && this.m_dataLength + diff >= this.m_buffer.Length)
        this.GrowBuffer(this.m_dataLength + diff);
      InfoCardTrace.Assert((uint) num3 <= (uint) this.m_buffer.Length && num3 >= offset, "NextIndex value is corrupt when attempting to ensure space for object");
      InfoCardTrace.Assert((uint) num4 <= (uint) this.m_buffer.Length, "NextNewIndex value is corrupt when attempting to ensure space for object");
      if (this.m_dataLength != num3)
      {
        InfoCardTrace.Assert(this.m_dataLength - num3 > 0, "NextIndex data is corrupt when attempting to ensure space for object in middle of content.");
        Array.Copy((Array) this.m_buffer, num3, (Array) this.m_buffer, num4, this.m_dataLength - num3);
        this.m_dataLength += diff;
        InfoCardTrace.Assert(this.m_dataLength >= 0, "DataLength has been corrupted.");
        if (diff < 0)
        {
          InfoCardTrace.Assert(this.m_dataLength <= this.m_buffer.Length, "DataLength is larger than allocated buffer.");
          Array.Clear((Array) this.m_buffer, this.m_dataLength, Math.Abs(diff));
        }
        this.UpdateMasterIndex(Math.Min(num3, num4), diff);
      }
      else
        this.m_dataLength += diff;
      InfoCardTrace.Assert(this.m_dataLength <= this.m_buffer.Length, "DataLength is larger than allocated buffer.");
      return num1;
    }

    private void GrowMasterIndex()
    {
      int num1 = this.m_masterIndex.Length / 4;
      int increaseByPercent = Utility.CalculateIncreaseByPercent(this.m_masterIndex.Length, 4, 5);
      InfoCardTrace.Assert(increaseByPercent > this.m_masterIndex.Length, "NewSize must be greater than old size to grow master index buffer");
      byte[] numArray = new byte[increaseByPercent];
      Array.Copy((Array) this.m_masterIndex, 0, (Array) numArray, 0, this.m_masterIndex.Length);
      Array.Clear((Array) this.m_masterIndex, 0, this.m_masterIndex.Length);
      this.m_masterIndex = numArray;
      int num2 = this.m_masterIndex.Length / 4;
      for (int index = num1 + 1; index < num2; ++index)
        this.m_freeList.Put(index);
    }

    private void GrowBuffer(int spaceRequired)
    {
      InfoCardTrace.Assert(spaceRequired >= this.m_buffer.Length, "RequireSpace must be greater than old size to grow data buffer");
      int num = spaceRequired;
      int length = num + num / 20;
      InfoCardTrace.Assert(length >= spaceRequired, "NewLength must be greater than required space to grow data buffer");
      byte[] numArray = new byte[length];
      Array.Copy((Array) this.m_buffer, 0, (Array) numArray, 0, this.m_buffer.Length);
      Array.Clear((Array) this.m_buffer, 0, this.m_buffer.Length);
      this.m_buffer = numArray;
      InfoCardTrace.Assert(this.m_dataLength <= this.m_buffer.Length, "DataLength Corrupt or buffer growth failed.");
    }

    private unsafe int GetOffset(ref int id)
    {
      if (id <= 0)
      {
        if (this.m_dataLength <= 0)
          this.m_dataLength = 8;
        int dataLength = this.m_dataLength;
        id = this.GetNextLocalId(dataLength);
        return dataLength;
      }
      fixed (byte* numPtr = &this.m_masterIndex[0])
      {
        InfoCardTrace.Assert(((int*) numPtr)[id] >= 0, "Master Index offset value is invalid");
        return ((int*) numPtr)[id];
      }
    }

    private unsafe void RemoveFromMasterIndex(int id)
    {
      InfoCardTrace.Assert(id > 0, "LocalId is invalid");
      fixed (byte* numPtr = &this.m_masterIndex[0])
      {
        *(int*) (numPtr + ((IntPtr) id * 4).ToInt64()) = 0;
        this.m_freeList.Put(id);
      }
    }

    private unsafe void UpdateMasterIndex(int offset, int diff)
    {
      InfoCardTrace.Assert(offset > 0, "Offset is invalid");
      InfoCardTrace.Assert(diff != 0, "Difference is invalid");
      int num = this.m_masterIndex.Length / 4;
      fixed (byte* numPtr1 = &this.m_masterIndex[0])
      {
        for (int index = 1; index < num; ++index)
        {
          InfoCardTrace.Assert((int*) numPtr1 + index - (int*) numPtr1 <= (long) this.m_masterIndex.Length, "Pointer has walked past the end of the allocated buffer.");
          if (((int*) numPtr1)[index] > 0 && ((int*) numPtr1)[index] >= offset)
          {
            int* numPtr2 = (int*) numPtr1 + index;
            *numPtr2 = *numPtr2 + diff;
          }
        }
      }
    }

    private unsafe int GetNextLocalId(int offset)
    {
      InfoCardTrace.Assert(offset >= 0, "The offset can not be negative.");
      int next = this.m_freeList.GetNext();
      if (next > 0)
      {
        InfoCardTrace.Assert(null != this.m_masterIndex, "m_masterIndex must not be null by now");
        InfoCardTrace.Assert(this.m_masterIndex.Length > 0, "m_masterIndex must not be empty.");
        fixed (byte* numPtr = &this.m_masterIndex[0])
        {
          *(int*) (numPtr + ((IntPtr) next * 4).ToInt64()) = offset;
          return next;
        }
      }
      else
      {
        int num = this.m_masterIndex.Length / 4;
        this.GrowMasterIndex();
        InfoCardTrace.Assert(this.m_masterIndex.Length / 4 > num && num > 0, "Calculated count is corrupt or invalid.");
        fixed (byte* numPtr = &this.m_masterIndex[0])
          *(int*) (numPtr + ((IntPtr) num * 4).ToInt64()) = offset;
        return num;
      }
    }

    public void ThrowIfNotOpen()
    {
      if (!this.m_isOpen)
        throw InfoCardTrace.ThrowHelperError((Exception) new ObjectDisposedException(nameof (IndexedDataBuffer)));
    }
  }
}
